home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / swagg_m.zip / MISC.SWG / 0036_Writing To EXE File.pas < prev    next >
Pascal/Delphi Source File  |  1993-08-27  |  9KB  |  258 lines

  1. {
  2. > How are you saaving the CFG into the .EXE?? Mind posting some code that wil
  3. > save the CFG to the EXE?(When you get all your bugs fixed!)
  4.  
  5. I use these routines in my self-modifying .EXE's. They work pretty good.
  6. }
  7.  
  8. Unit WritExec;
  9.  
  10.   { ==================================================================
  11.  
  12.                                Unit: WritExec
  13.                              Author: David Doty
  14.                                      Skipjack Software
  15.                                      Columbia, Maryland
  16.                CompuServe User I.D.: 76244,1043
  17.  
  18.     This Unit is based on a previously published Program:
  19.  
  20.                             Program: AutoInst v2.0
  21.                              Author: David Dubois
  22.                                      Zelkop Software
  23.                                      Halifax, Nova Scotia
  24.                CompuServe User I.D.: 71401,747
  25.                   Date last revised: 1988.04.24
  26.  
  27.     ==================================================================
  28.  
  29.     This source code is released to the public domain.  if further changes
  30.     are made, please include the above credits in the distributed code.
  31.  
  32.     This Unit allows a Program to change the value of a Typed Constant in its
  33.     own .EXE File.  When the Program is run again, the data will be initialized
  34.     to the new value.  No external configuration Files are necessary.
  35.  
  36.     Uses
  37.  
  38.     Examples of the usefulness of this technique would be:
  39.  
  40.     o   A Program that allows the user to change default display colors.
  41.  
  42.     o   A Program that keeps track of a passWord that the user can change.
  43.  
  44.     HOW IT WORKS
  45.  
  46.     You don't have to understand all the details in order to use this
  47.     technique, but here they are.
  48.  
  49.     The data to be changed must be stored in a TurboPascal Typed
  50.     Constant.  In all effect, a Typed Constant is actually a pre-
  51.     initialized Variable.  It is always stored in the Program's Data
  52.     Segment.  The data can be of any Type.
  53.  
  54.     First, the Procedure finds the .EXE File by examining the Dos command
  55.     line, stored With the copy of the Dos environment For the Program.  This
  56.     allows the Program to find itself no matter where is resides on disk and
  57.     no matter how its name is changed by the user.
  58.  
  59.     The unTyped File is opened With a Record size of 1. This allows us
  60.     to read or Write a String of Bytes using BlockRead and BlockWrite.
  61.  
  62.     As documented in the Dos Technical Reference, the size of the .EXE
  63.     header, in paraGraphs (a paraGraph is 16 Bytes), is stored as a
  64.     two-Byte Word at position 8 of the File.  This is read into the
  65.     Variable HeaderSize.
  66.  
  67.     The next step is to find the position of the Typed Constant in the
  68.     .EXE File. This requires an understanding of the Turbo Pascal 4.0
  69.     memory map, documented on the first and second pages of the Inside
  70.     Turbo Pascal chapter. (That's chapter 26, pages 335 and 336 in my
  71.     manual.)
  72.  
  73.     First, find the address in memory where the Typed Constant is
  74.     stored. This can be done in Turbo Pascal by using the Seg and Ofs
  75.     Functions. Next find the segment of the PSP (Program segment
  76.     prefix). This should always be the value returned by PrefixSeg.
  77.     That will mark the beginning of the Program in memory. The
  78.     position of the Typed Constant in the .EXE image should be the
  79.     number of Bytes between these two places in memory. But ...
  80.  
  81.     But, two corrections must be made. First, the PSP is not stored in
  82.     the .EXE File. As mentioned on page 335, the PSP is always 256
  83.     Bytes. We must subtract that out. Secondly, there is the .EXE File
  84.     header. The size of this has already been read in and must be
  85.     added in to our calculations.
  86.  
  87.     Once the position has been determined, the data stored in the
  88.     Typed Constant is written in one fell swoop using a BlockWrite.
  89.     This replaces the original data, so that the next time the Program
  90.     is run, the new values will used.
  91.  
  92.     LIMITATIONS
  93.  
  94.     You cannot use MicroSoft's EXEPACK on the .EXE File, or any other
  95.     packing method I know of. This may change the position, or even
  96.     the size of the Typed Constant in the File image.
  97.  
  98.     NOTES
  99.  
  100.     Since Typed Constants are always stored in the data segment, the
  101.     Function call to Seg( ObjectToWrite ) can be replaced With DSeg. I
  102.     prefer using Seg since it is more descriptive.
  103.  
  104.     One might think that Cseg can used as an alternative to using
  105.     PrefixSeg and subtracting 256. This will work only if the code
  106.     resides in the main Program. If, on the other hand, the code is
  107.     used in a Unit, PrefixSeg must be used as described here. You
  108.     might as well use PrefixSeg and save yourself some headaches.
  109.  
  110.     if you have any comments or questions we would be glad to hear
  111.     them. if you're on CompuServe, you can EasyPlex a letter to
  112.     76244,1043 or 71401,747. Or leave a message on the Borland Programmer's A
  113.     Forum (GO BPROGA). Or, you can Write to
  114.  
  115.                          Skipjack Software
  116.                          P. O. Box 61
  117.                          Simpsonville Maryland 21150
  118.  
  119.                             or
  120.  
  121.                          Zelkop Software
  122.                          P.O. Box 5177
  123.                          Armdale, N.S.
  124.                          Canada
  125.                          B3L 4M7
  126.  
  127.     ==================================================================}
  128.  
  129.  
  130. Interface
  131.  
  132. Function GetExecutableName : String;
  133. {  This Function returns the full drive, path, and File name of the application
  134.    Program that is running.  This Function is of more general interest than
  135.    just For writing into the EXE File.
  136.  
  137.    NOTE: THIS Function WILL ONLY WORK UNDER Dos 3.X + !!! }
  138.  
  139. Function WriteToExecutable(Var ObjectToWrite; ObjectSize : Word) : Integer;
  140. {  This Procedure modifies the EXE File on disk to contain changes to Typed
  141.    Constants.  NOTE - the Object MUST be a Typed Constant.  It may be found
  142.    in any part of the Program (i.e., main Program or any Unit).  The call is
  143.    made by unTyped address, to allow any kind of Object to be written.  The
  144.    Function returns the Dos error code from the I/O operation that failed
  145.    (if any did); if all operations were successful, the Function returns 0. }
  146.  
  147. Implementation
  148.  
  149. Function GetExecutableName : String;
  150. Type
  151.   Environment = Array[0..32766] of Char;
  152. Const
  153.   NullChar : Char = #0;
  154.   SearchFailed = $FFFF;
  155. Var
  156.   MyEnviron   : ^Environment;
  157.   Loop        : Word;
  158.   TempWord    : Word;
  159.   EnvironPos  : Word;
  160.   FilenamePos : Word;
  161.   TempString  : String;
  162. begin { Function GetExecutableName }
  163.   { Get Pointer to Dos environment }
  164.   MyEnviron := Ptr(MemW[PrefixSeg : $2C], 0);
  165.  
  166.   { Look For end of environment }
  167.   EnvironPos := SearchFailed;
  168.   Loop := 0;
  169.  
  170.   While Loop <= 32767 DO
  171.   begin
  172.     if MyEnviron^[ Loop ] = NullChar then
  173.       if MyEnviron^[ Loop + 1 ] = NullChar then
  174.       begin { found two nulls - this is end of environment }
  175.         EnvironPos := Loop;
  176.         Loop := 32767
  177.       end; { found two nulls }
  178.     Inc(Loop);
  179.   end; { While Loop }
  180.  
  181.   if EnvironPos = SearchFailed then
  182.     GetExecutableName := ''
  183.   else
  184.   begin { found end of environment - now look For path/File of exec }
  185.     EnvironPos  := EnvironPos + 4;
  186.     FilenamePos := SearchFailed;
  187.     TempWord    := EnvironPos;
  188.     Loop := 0;
  189.  
  190.     While Loop <= 127 DO
  191.     begin
  192.       if MyEnviron^[TempWord] = NullChar then
  193.       begin { found a null - this is end of path/File of exec }
  194.         FilenamePos := Loop;
  195.         Loop := 127
  196.       end; { found a null }
  197.       Inc(Loop);
  198.       Inc(TempWord);
  199.     end; { While Loop }
  200.  
  201.     if FilenamePos = SearchFailed then
  202.       GetExecutableName := ''
  203.     else
  204.     begin { found executable name - move into return String }
  205.       TempString[0] := Chr(FilenamePos);
  206.       Move(MyEnviron^[EnvironPos], TempString[1], FilenamePos);
  207.       GetExecutableName := TempString;
  208.     end; { found executable name }
  209.   end; { found environment end }
  210. end; { Function GetExecutableName }
  211.  
  212.  
  213. Function WriteToExecutable(Var ObjectToWrite; ObjectSize : Word ) : Integer;
  214. Const
  215.   PrefixSize = 256; { number of Bytes in the Program Segment Prefix }
  216. Var
  217.   Executable : File;
  218.   HeaderSize : Word;
  219.   ErrorCode  : Integer;
  220. begin
  221.   Assign(Executable, GetExecutableName);
  222.   {$I-}
  223.   Reset(Executable, 1);
  224.   ErrorCode := IOResult;
  225.  
  226.   if ErrorCode = 0 then
  227.   begin { seek position of header size in EXE File }
  228.     Seek(Executable, 8);
  229.     ErrorCode := IOResult;
  230.   end; { seek header }
  231.  
  232.   if ErrorCode = 0 then
  233.   begin { read header size in EXE File }
  234.     BlockRead(Executable, HeaderSize, SizeOf(HeaderSize));
  235.     ErrorCode := IOResult;
  236.   end; { read header }
  237.  
  238.   if ErrorCode = 0 then
  239.   begin { seek position of Object in EXE File }
  240.     Seek(Executable,
  241.          LongInt(16) * (HeaderSize + Seg(ObjectToWrite) - PrefixSeg) +
  242.          Ofs(ObjectToWrite) - PrefixSize);
  243.     ErrorCode := IOResult;
  244.   end; { Seek Object position in File }
  245.  
  246.   if ErrorCode = 0 then
  247.   begin { Write new passWord in EXE File }
  248.     BlockWrite(Executable, ObjectToWrite, ObjectSize);
  249.     ErrorCode := IOResult;
  250.   end; { Write new passWord }
  251.  
  252.   Close(Executable);
  253.   WriteToExecutable := ErrorCode;
  254.  
  255. end; { Function WriteToExecutable }
  256.  
  257. end. { Unit WritExec }
  258.